﻿/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * ZIP Header Base Class
 * 		Provides a base implementation for classes which represent file information
 * header data.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using Adaptive.Win32;

namespace Adaptive.Foundation.IO.Zip
{

	/// <summary>
	/// Provides a base implementation for classes which represent file information
	/// header data.
	/// </summary>
	/// <remarks>
	/// This class provides the same basic fields for the Zip local file header and the
	/// zip directory entry header.
	/// </remarks>
	public abstract class ZipHeaderBase : ZipIoBase
	{

		/*--------------------------------------------------------------------------------
		 * Private Member Declarations
		 *------------------------------------------------------------------------------*/
		#region Private Member Declarations
		//Entry signature value. 
		private byte[] _signature;
		//Version that created the item.
		private ZipCreatedByVersion _versionMadeBy;
		//Version needed to read the item.
		private ZipRequiredVersion _versionNeeded;
		//Bit flags.
		private ZipBitFlags _bitFlags;
		//Compression method.
		private CompressionMethods _compressionmethod = CompressionMethods.Deflated;
		//Last modified date and time values.
		private MsDosDate _lastModifiedDateTime;
		//Cyclic redundancy check value.
		private uint _crc32;
		//Size of data when compressed.
		private int _compressedSize;
		//Size of data when uncompressed.
		private int _uncompressedSize;
		//Length of the file name value.
		private short _fileNameLength;
		//Length of the extra data field.
		private short _extraDataLength;
		//File name.
		private string _fileName = string.Empty;
		//Extra data field.
		private byte[] _extraData;
		#endregion

		/*--------------------------------------------------------------------------------
	 * Constructor / Dispose / Destructor Methods
	 *------------------------------------------------------------------------------*/
		#region Constructor / Dispose / Destructor Methods
		/// <summary>
		/// Initializes a new instance of the <see cref="ZipHeaderBase" /> class.
		/// </summary>
		/// <remarks>
		/// This is the default constructor.
		/// </remarks>
		protected ZipHeaderBase()
			: base()
		{
			_versionMadeBy = new ZipCreatedByVersion(ZipOriginationType.WindowsNTFS, 21);
			_versionNeeded = new ZipRequiredVersion(21);
			_bitFlags = new ZipBitFlags();
			_signature = CreateSignatureDefaultValue();
		}
		/// <summary>
		/// Releases unmanaged and - optionally - managed resources
		/// </summary>
		/// <param name="disposing"><b>true</b> to release both managed and unmanaged resources;
		/// <b>false</b> to release only unmanaged resources.</param>
		protected override void Dispose(bool disposing)
		{
			if (!(Disposed) && (disposing))
			{
				if (_extraData != null)
					Array.Clear(_extraData, 0, _extraData.Length);
			}

			_fileName = null;
			_extraData = null;
			_bitFlags = null;
			_versionMadeBy = null;
			_versionNeeded = null;
			base.Dispose(disposing);
		}
		#endregion

		/*--------------------------------------------------------------------------------
	 * Public Properties
	 *------------------------------------------------------------------------------*/
		#region Public Properties
		/// <summary>
		/// Gets or sets the 4 byte signature value.
		/// </summary>
		/// <value>
		/// The signature value as a byte array.
		/// </value>
		public byte[] Signature
		{
			get
			{
				return _signature;
			}
			set
			{
				_signature = new byte[4];
				Array.Copy(value, _signature, 4);
			}
		}
		/// <summary>
		/// Gets the reference to the created-by version data.
		/// </summary>
		/// <value>
		/// A <see cref="ZipCreatedByVersion"/> instance containing the versioning data.
		/// </value>
		public ZipCreatedByVersion VersionMadeBy
		{
			get
			{
				return _versionMadeBy;
			}
		}
		/// <summary>
		/// Gets the reference to the required-to-read-file version data.
		/// </summary>
		/// <value>
		/// A <see cref="ZipRequiredVersion"/> instance containing the versioning data.
		/// </value>
		public ZipRequiredVersion VersionNeeded
		{
			get
			{
				return _versionNeeded;
			}
		}
		/// <summary>
		/// Gets the reference to the bit flags.
		/// </summary>
		/// <value>
		/// A <see cref="ZipBitFlags"/> instance used to read, write, and interpret
		/// the bit flags field.
		/// </value>
		public ZipBitFlags BitFlags
		{
			get
			{
				return _bitFlags;
			}
		}
		/// <summary>
		/// Gets or sets the method of compression used on the internal data.
		/// </summary>
		/// <value>
		/// A <see cref="CompressionMethod"/> enumerated value indicating the 
		/// type of compression algorithm used.
		/// </value>
		public CompressionMethods CompressionMethod
		{
			get
			{
				return _compressionmethod;
			}
			set
			{
				_compressionmethod = value;
			}
		}
		/// <summary>
		/// Gets or sets the last modified time value.
		/// </summary>
		/// <value>The last modified time value.</value>
		public ushort LastModifiedTime
		{
			get
			{
				return _lastModifiedDateTime.Time;
			}
			set
			{
				_lastModifiedDateTime.Time = value;
			}
		}
		/// <summary>
		/// Gets the last modified date and time value.
		/// </summary>
		/// <value>
		/// A <see cref="DateTime"/> value.
		/// </value>
		public DateTime LastModifiedDateAndTime
		{
			get
			{
				return DateTime.FromFileTime(Kernel32.DosDateToFileTime(_lastModifiedDateTime));
			}
		}
		/// <summary>
		/// Gets or sets the last modified date value.
		/// </summary>
		/// <value>The last modified date value.</value>
		public ushort LastModifiedDate
		{
			get
			{
				return _lastModifiedDateTime.Date;
			}
			set
			{
				_lastModifiedDateTime.Date = value;
			}
		}
		/// <summary>
		/// Gets or sets the cyclic-redundancy-check value.
		/// </summary>
		/// <value>
		/// The value of the CRC-32 calculation on the data.
		/// </value>
		public uint Crc32
		{
			get
			{
				return _crc32;
			}
			set
			{
				_crc32 = value;
			}
		}
		/// <summary>
		/// Gets or sets the size of the compressed data/
		/// </summary>
		/// <value>The size of the compressed data, in bytes.</value>
		public int CompressedSize
		{
			get
			{
				return _compressedSize;
			}
			set
			{
				_compressedSize = value;
			}
		}
		/// <summary>
		/// Gets or sets the size of the uncompressed data.
		/// </summary>
		/// <value>The size of the uncompressed data, in bytes.</value>
		public int UncompressedSize
		{
			get
			{
				return _uncompressedSize;
			}
			set
			{
				_uncompressedSize = value;
			}
		}
		/// <summary>
		/// Gets or sets the length of the file name.
		/// </summary>
		/// <value>The length of the file name.</value>
		public short FileNameLength
		{
			get
			{
				return _fileNameLength;
			}
			set
			{
				_fileNameLength = value;
			}
		}
		/// <summary>
		/// Gets or sets the length of the extra data, in bytes.
		/// </summary>
		/// <value>The length of the extra data.</value>
		public short ExtraDataLength
		{
			get
			{
				return _extraDataLength;
			}
			set
			{
				_extraDataLength = value;
			}
		}
		/// <summary>
		/// Gets or sets the name of the file.
		/// </summary>
		/// <value>The name of the file.</value>
		public string FileName
		{
			get
			{
				return _fileName;
			}
			set
			{
				if (String.IsNullOrEmpty(value))
				{
					_fileNameLength = 0;
					_fileName = String.Empty;
				}
				else
				{
					_fileName = value;
					_fileNameLength = (short)(value.Length);
				}

			}
		}
		/// <summary>
		/// Gets or sets the extra data.
		/// </summary>
		/// <value>
		/// The extra data as a byte array.
		/// </value>
		public byte[] ExtraData
		{
			get
			{
				return _extraData;
			}
			set
			{
				if (value == null)
					_extraData = null;
				else
				{
					_extraData = new byte[value.Length];
					Array.Copy(value, _extraData, value.Length);
				}
			}
		}
		#endregion

		/*--------------------------------------------------------------------------------
	 * Protected Methods
	 *------------------------------------------------------------------------------*/
		#region Protected Methods
		/// <summary>
		/// Creates the 4 data bytes that make up the signature value used to identify
		/// the header.
		/// </summary>
		/// <returns>
		/// The signature value as a 4-byte array.
		/// </returns>
		protected abstract byte[] CreateSignatureDefaultValue();
		/// <summary>
		/// Reads the version-made-by versioning value.
		/// </summary>
		/// <param name="reader">
		/// An open <see cref="BinaryReader"/> used to read the contents 
		/// </param>
		/// <remarks>
		/// This allows different child implementations to read (or not read) and interpret
		/// this data as needed.
		/// </remarks>
		protected abstract void ReadVersionMadeBy(BinaryReader reader);
		/// <summary>
		/// Reads any fields not defined on the base class.
		/// </summary>
		/// <param name="reader">
		/// An open <see cref="BinaryReader"/> used to read the contents 
		/// </param>
		/// <remarks>
		/// This allows different child implementations to read (or not read) and interpret
		/// this data as needed.
		/// </remarks>
		protected abstract void ReadExtraFields(BinaryReader reader);
		/// <summary>
		/// Reads any remaining data in the structure.
		/// </summary>
		/// <param name="reader">
		/// An open <see cref="BinaryReader"/> used to read the contents 
		/// </param>
		/// <remarks>
		/// This allows different child implementations to read (or not read) and interpret
		/// this data as needed.
		/// </remarks>
		protected abstract void ReadRemainingData(BinaryReader reader);
		/// <summary>
		/// Writes the made-by versioning data.
		/// </summary>
		/// <param name="writer">
		/// An open <see cref="BinaryWriter"/> used to writer the contents 
		/// </param>
		/// <remarks>
		/// This allows different child implementations to write (or not write) 
		/// this data as needed.
		/// </remarks>
		protected abstract void WriteVersionMadeBy(BinaryWriter writer);
		/// <summary>
		/// Writes any fields not defined on the base class.
		/// </summary>
		/// <param name="writer">
		/// An open <see cref="BinaryWriter"/> used to writer the contents 
		/// </param>
		/// <remarks>
		/// This allows different child implementations to write (or not write) 
		/// this data as needed.
		/// </remarks>
		protected abstract void WriteExtraFields(BinaryWriter writer);
		/// <summary>
		/// Writes any remaining data in the structure.
		/// </summary>
		/// <param name="writer">
		/// An open <see cref="BinaryWriter"/> used to writer the contents 
		/// </param>
		/// <remarks>
		/// This allows different child implementations to write (or not write) 
		/// this data as needed.
		/// </remarks>
		protected abstract void WriteRemainingData(BinaryWriter writer);
		#endregion

		/*--------------------------------------------------------------------------------
	 * Public Methods / Functions
	 *------------------------------------------------------------------------------*/
		#region Public Methods / Functions
		/// <summary>
		/// Reads the content of the object from the underlying stream.
		/// </summary>
		/// <param name="reader">
		/// An open <see cref="BinaryReader"/> used to read the data content.
		/// </param>
		/// <returns>
		/// <b>true</b> if the operation is successful; otherwise, returns <b>false</b>.
		/// </returns>
		public override bool Load(BinaryReader reader)
		{
			byte[] bufferData = null;			//Data buffer.
			bool returnValue = false;			//Return value.
			//PKZip - Shared elements between local file header and directory entry:
			//
			//Local File Header Signature	- 4 bytes.
			//Version						- 2 bytes.
			//General Purpose Bit Flag		- 2 bytes.
			//Compression Method				- 2 bytes.
			//Last Modified Time				- 2 bytes.
			//Last Modified Date				- 2 bytes.
			//CRC-32							- 4 bytes.
			//Compressed Size				- 4 bytes.
			//Uncompressed size				- 4 bytes.
			//File name length				- 2 bytes.
			//Extra data field length		- 2 bytes.
			//File name						- variable
			//Extra data field				- variable

			//Read the header data.
			try
			{
				_signature = reader.ReadBytes(4);
				ReadVersionMadeBy(reader);
				_versionNeeded.Value = reader.ReadInt16();
				_bitFlags.Value = reader.ReadInt16();
				_compressionmethod = (CompressionMethods)(reader.ReadInt16());
				_lastModifiedDateTime = new MsDosDate();
				_lastModifiedDateTime.Time = reader.ReadUInt16();
				_lastModifiedDateTime.Date = reader.ReadUInt16();
				_crc32 = reader.ReadUInt32();
				_compressedSize = reader.ReadInt32();
				_uncompressedSize = reader.ReadInt32();
				_fileNameLength = reader.ReadInt16();
				_extraDataLength = reader.ReadInt16();

				//Read any other defined fields.
				ReadExtraFields(reader);

				//Read the file name value.
				bufferData = reader.ReadBytes(_fileNameLength);
				_fileName = System.Text.ASCIIEncoding.ASCII.GetString(bufferData);

				//Read the extra field data.
				_extraData = reader.ReadBytes(_extraDataLength);

				//Read any data that is left.
				ReadRemainingData(reader);
				returnValue = true;
			}
			catch (IOException ex)
			{
				OnReadException(new IoExceptionEventArgs(ex));
			}

			return returnValue;
		}
		/// <summary>
		/// Writes the content of the object to the underlying stream.
		/// </summary>
		/// <param name="writer">
		/// An open <see cref="BinaryWriter"/> used to write the data content.
		/// </param>
		/// <returns>
		/// <b>true</b> if the operation is successful; otherwise, returns <b>false</b>.
		/// </returns>
		public override bool Save(BinaryWriter writer)
		{
			bool returnValue = false;			//Return value

			//PKZip - Shared elements between local file header and directory entry:
			//
			//Local File Header Signature	- 4 bytes.
			//Version						- 2 bytes.
			//General Purpose Bit Flag		- 2 bytes.
			//Compression Method				- 2 bytes.
			//Last Modified Time				- 2 bytes.
			//Last Modified Date				- 2 bytes.
			//CRC-32							- 4 bytes.
			//Compressed Size				- 4 bytes.
			//Uncompressed size				- 4 bytes.
			//File name length				- 2 bytes.
			//Extra data field length		- 2 bytes.
			//File name						- variable
			//Extra data field				- variable

			//Write the header data.
			try
			{
				writer.Write(_signature);
				WriteVersionMadeBy(writer);
				writer.Write(_versionNeeded.Value);
				writer.Write(_bitFlags.Value);
				writer.Write((short)_compressionmethod);
				writer.Write(_lastModifiedDateTime.Time);
				writer.Write(_lastModifiedDateTime.Date);
				writer.Write(_crc32);
				writer.Write(_compressedSize);
				writer.Write(_uncompressedSize);
				writer.Write(_fileNameLength);
				writer.Write(_extraDataLength);

				//Write any other defined fields.
				WriteExtraFields(writer);

				//Write the file name value.
				writer.Write(System.Text.ASCIIEncoding.ASCII.GetBytes(_fileName));

				//Write the extra field data.
				if (_extraData != null)
					writer.Write(_extraData);

				//Write any data that is left.
				WriteRemainingData(writer);

				writer.Flush();
				returnValue = true;
			}
			catch (IOException ex)
			{
				OnWriteException(new IoExceptionEventArgs(ex));
			}

			return returnValue;

		}
		#endregion
	}
}